package tul.cot.util;

import java.util.Arrays;

import org.apache.commons.lang.ArrayUtils;

public class Histogram {
	
	private String label;
	private double min;
	private double max;
	private int buckets;
	private final double bLen; //bucket length
	int[] counts;
	double[] densityValues;

	public Histogram(String label, double min, double max, int buckets) {
		if (max <= min)
			throw new IllegalArgumentException("max <= min");
		if (buckets <= 0)
			throw new IllegalArgumentException("buckets <= 0");
		
		this.label = label;
		this.min = min;
		this.max = max;
		this.buckets = buckets;
		this.bLen = (max - min) / buckets;
		
		this.counts = new int[buckets];
	}
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Histogram))
			return false;
		
		Histogram other = (Histogram) obj;
		if (!this.label.equals(other.label)) return false;
		if (this.min != other.min || this.max != other.max) return false;
		if (this.buckets != other.buckets) return false;
		if (!Arrays.equals(this.counts, other.counts)) return false;
		if (!Arrays.equals(this.densityValues, other.densityValues)) return false;
		
		return true;
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		
		sb.append(String.format("label: %s\n", label));
		sb.append(String.format("min: %f\tmax: %f\n", min, max));
		sb.append(String.format("buckets: %d\n", buckets));
		sb.append(String.format("counts: %s\n", ArrayUtils.toString(counts)));
		sb.append(String.format("density: %s\n", ArrayUtils.toString(densityValues)));
		
		return sb.toString();
	}
	
	public String getLabel() {
		return label;
	}
	
	public int[] getCounts() {
		return Arrays.copyOf(counts, counts.length);
	}
	
	public double[] getDensityValues() {
		if (densityValues != null)
			return densityValues;
		
		densityValues = new double[buckets];
		double normFactor = getNormFactor();
		
		for (int x=0; x<buckets; ++x) {
			densityValues[x] = ((double)counts[x]) * normFactor;
		}
		
		return densityValues;
	}

	private double getNormFactor() {
		double area = 0;
		for (int cnt: counts) {
			area += bLen * cnt;
		}
		
		return 1.0/area;
	}

	public void addValues(double[] values) {
		for (double v: values)
			addValue(v);
	}

	public void addValue(double v) {
		if (v < min || v > max)
			throw new IllegalArgumentException(String.format("v(%f) should be in range [%f,%f]", v, min, max));
		
		counts[getBucket(v)]++;
	}

	// [) [) .. [) []
//	private int getBucket(double v) {		
//		int ret = (int) Math.floor((v-min)/bLen);
//		if (ret == buckets)
//			--ret;
//		return ret;
//	}
	
	// [] (] ... (] (]
	private int getBucket(double v) {
		int ret = ((int) Math.ceil((v-min)/bLen)) - 1;
		
		if (ret == -1)
			ret = 0;
		
		return ret;
	}
	
	

}
